<?php

namespace extend\bridgecli\client;

use Swoole\Coroutine;
use Swoole\Event;
use Swoole\Server;

class HttpClient
{
    /**
     * 连接管理
     * @var \extend\bridgecli\BridgeProxy
     */
    protected $manage = null;
    /**
     * 地址
     * @var array
     */
    public $addr = [];
    /**
     * @var Coroutine\Socket
     */
    public $socket = null; // 代理最终的目标连接


    public $lastTime = 0; // 最后交互时间

    public function __construct(mixed $manage)
    {
        $this->manage = $manage;
        $this->lastTime = time();
    }

    /**
     * 连接消息
     * @param \Swoole\Server $server
     * @param int $tofd
     * @param mixed $data
     * @return void
     */
    public function connect(mixed $server, int $tofd, mixed $data)
    {
        try {
            $this->addr = $data;
            $this->addr['tofd'] = $tofd;
            $urlData = parse_url($this->addr['host']);
            $host = $urlData['host'];
            $port = $urlData['port'] ?? 80;
            $httpVer = explode('/', $this->addr['version'])[1];

            list($ip, $type, $domain) = $this->manage->gethostbyname($host);
            $this->addr['target_ip'] = $ip;
            $this->addr['target_port'] = $port;
            $this->addr['target_type'] = $type;
            $this->addr['target_domain'] = $domain;

            $socket = new Coroutine\Socket($domain, $type);
            $this->socket = $socket;

            if($type == SOCK_STREAM){
                if(!$this->socket->connect($ip, $port, 5)){
                    $server->send($tofd, "连接失败", 'close');
                    return false;
                }
                if($this->addr["method"] == 'CONNECT'){
                    $server->send($tofd, $this->addr['version']." 200 Connection Established\r\n\r\n");
                }else{
                    $this->socket->send($this->addr['body']);
                }
                $this->wait($server);
                return true;
            }else{
                if($data["body"] == false){
                    $server->send($tofd, $httpVer." 200 Connection Established\r\n\r\n");
                }else{
                    $this->socket->sendto($ip, $port, $this->addr['body']);
                }
                $this->wait($server);
                return true;
            }
        }catch (\Throwable $e){
            //echo $e->getMessage().PHP_EOL.'#@ '.$e->getFile().':'.$e->getLine().PHP_EOL.$e->getTraceAsString().PHP_EOL;
        }
        return false;
    }

    public function send(mixed $server, int $tofd, string $data)
    {
        $data = base64_decode($data);
        $this->lastTime = time();
        if($this->addr['target_type'] == SOCK_STREAM){
            $this->socket->send($data);
        }else{
            $this->socket->sendto($this->addr['target_ip'], $this->addr['target_port'], $data);
        }
    }

    /**
     * 销毁掉
     * @param mixed $server
     * @param int $tofd
     * @return void
     */
    public function close(mixed $server, int $tofd)
    {
        if($this->socket){
            Event::del($this->socket);
            $this->socket->close();
        }
    }

    /**
     * @param Server $server
     * @return void
     */
    private function wait(mixed $server)
    {
        Event::add($this->socket, function (Coroutine\Socket $socket) use ($server){
            if($this->addr['target_type'] == SOCK_STREAM){
                $data = $socket->recv(65535);
            }else{
                $peer = null;
                $data = $socket->recvfrom($peer);
            }

            if (strlen($data) > 0) {
                Event::del($socket); // 先销毁事件
                $this->lastTime = time();
                $server->send($this->addr['tofd'], $data);
                $this->wait($server);
            }else{
                // 如果不是超时就应该断开
                if($socket->errCode != SOCKET_ETIMEDOUT || $data === ""){
                    $this->lastTime = 0;
                    Event::del($socket);
                    $socket->close();
                    $server->send($this->addr['tofd'], '连接异常断开', 'close');
                    unset($this->manage->httpProxyClients[$this->addr['tofd']]);
                }
            }
        });
    }
}